package com.lixw.study.aspects;

import com.lixw.study.annotations.RedisCache;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName: RedisCacheAspect
 * @Description: 缓存切面类
 * @Author: xuweiLi
 * @Create: 2025/7/31 10:18
 **/
@Slf4j
@Aspect
@Component
public class RedisCacheAspect {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Pointcut("@annotation(com.lixw.study.annotations.RedisCache)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object cacheAround(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("请求到达缓存切面类...");
        //获取目标方法的签名
        String simpleName = joinPoint.getTarget().getClass().getSimpleName();
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        //获取注解
        RedisCache annotation = method.getAnnotation(RedisCache.class);
        String keyPrefix = annotation.keyPrefix();
        String matchValue = annotation.matchValue();

        //获取目标方法参数
        Object[] args = joinPoint.getArgs();
        //获取参数名
        Parameter[] parameters = method.getParameters();
        log.info("方法参数为:{} , 参数列表:{} ", args, parameters);

        //解析key
        SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
        Expression expression = spelExpressionParser.parseExpression(matchValue);
        StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
        for (int i = 0; i < args.length; i++) {
            evaluationContext.setVariable(parameters[i].getName(), args[i]);
        }

        String redisKey = keyPrefix + ":" + expression.getValue(evaluationContext);
        log.info("解析到的redisKey为:{}", redisKey);

        //去redis检索数据
        BoundValueOperations<String, Object> redisKeyOps = redisTemplate.boundValueOps(redisKey);
        Object data = redisKeyOps.get();
        if (data != null) {
            log.info("redis缓存命中key:{},直接返回数据:{}", redisKey, data);
            return data;
        }

        //去查询数据库
        log.info("redis未命中key{},去数据库查询", redisKey);
        Object result = joinPoint.proceed();
        if (result != null) {
            log.info("数据库中检索到数据:{}", result);
            log.info("执行缓存补偿->> redis");
            redisKeyOps.set(result,3, TimeUnit.MINUTES);
        } else {
            log.warn("数据库中未查询到数据,目标类:{},目标方法:{}",simpleName,method.getName());
        }
        return result;
    }
}